package com.Spring.test_2_3_spring_principle;

import com.Spring.test_2_3_spring_principle.component.BeanLifeComponent;
import com.Spring.test_2_3_spring_principle.model.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

@SpringBootApplication
public class Test23SpringPrincipleApplication {

    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(Test23SpringPrincipleApplication.class, args);
//        User user = context.getBean(User.class);
//        user.setName("lisi");
//        System.out.println(user);
//        System.out.println("当前对象的地址: "+System.identityHashCode(user));
//
//        User user2 = context.getBean(User.class);
//        System.out.println(user2);
//        System.out.println("当前对象的地址: "+System.identityHashCode(user2));

        // 结果 user 和 user2 的地址都是一样的 , 所以 这两个代表是同一个对象
        // 而且这两个对象获取的构造方法也是一模一样的
        // 如果对 user 修改里面的属性名字
        //  user2 不修改
        // 运行之后发现结果还是一样的

        // 通过以上证明 拿到的都是一个对象
        // 从 Context 中获取对象, 得到的都是同一个
        // 这种行为模式, 就称为 bean 的作用域

        // 这种模式 就叫做单例模式 (每次生成的对象都是同一个)

        // Bean 默认的创建的就是单例模式

        // 同理还有多例模式, 每次生成的对象都不是同一个

        // ***** 在Spring中⽀持6种作⽤域，后4种在Spring MVC环境才⽣效 *****
        //1. singleton：单例作⽤域
        //2. prototype：原型作⽤域（多例作⽤域）
        //3. request：请求作⽤域
        //4. session：会话作⽤域
        //5. Application: 全局作⽤域
        //6. websocket：HTTP WebSocket 作⽤域

        // singleton 每个Spring IoC容器内同名称的bean只有⼀个实例(单例)(默认)
        // prototype 每次使⽤该bean时会创建新的实例(⾮单例)
        // request 每个HTTP 请求⽣命周期内, 创建新的实例(web环境中, 了解)
        // session 每个HTTP Session⽣命周期内, 创建新的实例(web环境中, 了解)
        // application 每个ServletContext⽣命周期内, 创建新的实例(web环境中, 了解)
        // websocket 每个WebSocket⽣命周期内, 创建新的实例(web环境中, 了解)

        // Application scope就是对于整个web容器来说, bean的作⽤域是ServletContext级别的. 这个和
        //singleton有点类似，区别在于: Application scope是ServletContext的单例, singleton是⼀个
        //ApplicationContext的单例. 在⼀个web容器中ApplicationContext可以有多个. (了解即可)


        // ***** Bean 的生命周期 *****
        // ⽣命周期指的是⼀个对象从诞⽣到销毁的整个⽣命过程, 我们把这个过程就叫做⼀个对象的⽣命周期.
        //Bean 的⽣命周期分为以下5个部分: (这里分为了五类,具体还要自己来说来研究)
        //1. 实例化(为Bean分配内存空间)
        //2. 属性赋值(Bean注⼊和装配, ⽐如 @AutoWired )
        //3. 初始化
        //a. 执⾏各种通知, 如 BeanNameAware , BeanFactoryAware ,
        //ApplicationContextAware 的接⼝⽅法.
        //b. 执⾏初始化⽅法
        //▪ xml定义 init-method
        //▪ 使⽤注解的⽅式 @PostConstruct
        //▪ 执⾏初始化后置⽅法( BeanPostProcessor )
        //4. 使⽤Bean
        // 5. 销毁Bean
        //a. 销毁容器的各种⽅法, 如 @PreDestroy , DisposableBean 接⼝⽅法, destroy-method.
        //实例化和属性赋值对应构造⽅法和setter⽅法的注⼊. 初始化和销毁是⽤⼾能⾃定义扩展的两个阶段,
        //可以在实例化之后, 类加载完成之前进⾏⾃定义"事件"处理.
        //⽐如我们现在需要买⼀栋房⼦, 那么我们的流程是这样的：
        //1. 先买房(实例化, 从⽆到有)
        //2. 装修(设置属性)
        //3. 买家电, 如洗⾐机, 冰箱, 电视, 空调等([各种]初始化,可以⼊住)；
        //4. ⼊住(使⽤ Bean)
        //5. 卖房(Bean 销毁

        BeanLifeComponent lifeComponent = context.getBean(BeanLifeComponent.class);
        lifeComponent.use();
        // 运行结果
        // 先执行构造方法
        // 再执行属性注入
        // 再执行初始化
        // 程序启动完之后 执行 use 方法
        // 程序关闭的时候 执行 destroy 方法

        // 如果不结束进程直接去销毁的话
        // ((AbstractApplicationContext)context).destroy(); // 就执行这个方法
        // 但是这个是不建议使用的, 不结束进程去销毁

        // ****** Spring的自动装配 ******
        //主要分以下两个⽅⾯:
        //1. Spring 是如何把对象加载到SpringIoC容器中的
        //2. SpringBoot 是如何实现的
    }

}
